package org.spring.springboot.component;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.spring.springboot.app.base.Menu;
import org.spring.springboot.app.base.R;
import org.spring.springboot.app.base.Type;
import org.spring.springboot.app.base.User;
import org.spring.springboot.component.annotation.AccessTokenVerify;
import org.spring.springboot.component.annotation.AccessTokenVerifyType;
import org.spring.springboot.util.RedisUtils;
import org.spring.springboot.util.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.List;

/**
 * Access Token拦截器
 */
@Component
public class WebInterceptorAdapter extends HandlerInterceptorAdapter {

    private final static Log log = LogFactory.getLog(WebInterceptorAdapter.class);

    @Value("${token.string}")
    private String token;

    @Autowired
    private RedisUtils redisUtils;

    private void tokenToUser(HttpServletRequest request) {
        String accessToken = request.getParameter(token);
        if (accessToken == null || accessToken.trim().length() == 0) {
            return;
        }
        User user = null;
        if (accessToken != null && accessToken.trim().length() > 0) {
            user = redisUtils.get(accessToken);
        }
        if (user != null) {
            ThreadLocalUtil.put(token, user);
            request.setAttribute("user", user);
        }
    }

    private boolean hasMenuPermission(List<Menu> menus, String permission) {
        if (menus == null || menus.isEmpty()) {
            return false;
        }
        for (Menu m : menus) {
            if (m.getPermission() == null || m.getPermission().trim().length() == 0) {
                continue;
            }
            if (m.getPermission().equals(permission)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasPermissions(User user, String[] permissions) {
        for (String permission : permissions) {
            if (permission == null) {
                continue;
            }
            permission = permission.trim();
            if(permission.length() == 0){
                continue;
            }
            if (!hasMenuPermission(user.getMenus(), permission)) {
                return false;
            }
        }
        return true;
    }

    private boolean validateAccessToken(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        AccessTokenVerify annotation = handlerMethod.getMethodAnnotation(AccessTokenVerify.class);
        HandlerMethod method = (HandlerMethod) handler;
        Class<?> clazz = handlerMethod.getMethod().getReturnType();
        if (annotation == null) { //方法上没有注解则取类上的注解
            annotation = method.getBeanType().getAnnotation(AccessTokenVerify.class);
        }
        if (annotation == null || annotation.type() == AccessTokenVerifyType.NONE) {
            return true;
        }
        if (annotation.type() == AccessTokenVerifyType.TOKEN) {
            String accessToken = request.getParameter(token);
            if (accessToken != null && accessToken.trim().length() > 0) {
                if (redisUtils.hasKey(accessToken)) {
                    if (annotation.RequiresPermissions().length == 0) {
                        return true;
                    }
                    User user = redisUtils.get(accessToken);
                    if(hasPermissions(user,annotation.RequiresPermissions())){
                        return true;
                    }
                }
            }
            response.setContentType("application/json;charset=utf-8");
            response.setCharacterEncoding("UTF-8");

            PrintWriter writer = null;
            try {
                R result = new R(Type.PowerValidateFail);
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                String ret = objectMapper.writeValueAsString(result);
                writer = response.getWriter();
                writer.print(ret);
                return false;
            } catch (Exception e) {
                log.error("respose writer error.", e);
            } finally {
                if (writer != null) {
                    try {
                        writer.flush();
                        writer.close();
                    } catch (Exception e1) {
                        log.error("writer close error.", e1);
                    }
                }
            }
        }
        return true;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        tokenToUser(request);
        boolean validateResult = this.validateAccessToken(request, response, handler);
        if (!validateResult) {
            return false;
        }
        return true;
    }
}
